import throttle from '@/utils/throttle'
import { PROJECT_PREFIX } from '@/constant/index'
//#region  ---------ts
type Timer = 'Timer'
type AnimationRes = `AnimationRes`

type scrollTimer = `${PROJECT_PREFIX}${Timer}`
type scrollAnimateRes = `${PROJECT_PREFIX}${AnimationRes}`

type allExtendKeys = scrollTimer | scrollAnimateRes

type scrollElement = HTMLElement & {
  [extendElementKey in allExtendKeys]: extendElementKey extends scrollTimer
    ? NodeJS.Timeout | undefined
    : { stop: () => Number } | undefined
}

type scrollUpOrDownWithAnimateOption = {
  Ele?: scrollElement
  targetPosition?: number
  step?: number
  spacingTime?: number
  isStopOnWheel?: boolean
  isStopOnTouchMove?: boolean
  finish?: () => void
}
type scrollUpOrDownWithAnimate = (
  options: scrollUpOrDownWithAnimateOption | {}
) => {
  stop: () => void
} | null
type scrollUpOrDownWithAnimateReturn = { stop: () => void } | null
//#endregion  ---------ts
/**
 * 使给定元素平滑地滚动到指定位置。
 *
 * 该函数通过动画方式使页面中的某个元素（如div）平滑地滚动到指定的位置。它可以通过设置参数来定义滚动的目标位置、滚动的步长、滚动的时间间隔等。
 *
 * @param {Object} options - 参数对象。
 * @param {Element} [options.Ele] - 需要滚动的元素，必须是window.Element的实例。
 * @param {number} [options.targetPosition=0] - 滚动的目标位置，默认为0。
 * @param {number} [options.step=10] - 每次滚动的步长，默认为10。
 * @param {number} [options.spacingTime=20] - 滚动步长之间的时间间隔（毫秒），默认为20。
 * @param {boolean} [options.isStopOnWheel=true] - 在滚动过程中，如果用户使用鼠标滚轮，是否停止自动滚动，默认为true。
 * @param {boolean} [options.isStopOnTouchMove=true] - 在滚动过程中，如果用户触摸移动设备，是否停止自动滚动，默认为true。
 * @param {Function} [options.finish=() => {}] - 滚动完成后执行的回调函数，默认为空函数(仅滚动完成后执行,中途被中断的不执行)。
 * @returns {Object} 返回一个对象，该对象包含一个stop方法(返回停止时的scrollTop)，用于停止滚动。
 */
function scrollUpOrDownWithAnimate({
  Ele,
  targetPosition = 0,
  step = 10,
  spacingTime = 20,
  isStopOnWheel = true,
  isStopOnTouchMove = true,
  finish = () => {}
}: scrollUpOrDownWithAnimateOption = {}): scrollUpOrDownWithAnimateReturn {
  if (!((Ele as any) instanceof window.Element) || Ele === undefined)
    return null

  const Timer: Timer = 'Timer'
  const AnimationRes: AnimationRes = 'AnimationRes'
  const timer = (PROJECT_PREFIX + Timer) as scrollTimer
  const resKeyName = (PROJECT_PREFIX + AnimationRes) as scrollAnimateRes
  if (targetPosition === Ele.scrollTop) return { stop: () => {} }
  function addEvent(Ele: scrollElement) {
    isStopOnTouchMove && Ele.addEventListener('touchmove', onTouchmove)
    isStopOnWheel && Ele.addEventListener('wheel', onWheel)
  }
  function removeEvent(Ele: scrollElement) {
    isStopOnTouchMove && Ele.removeEventListener('touchmove', onTouchmove)
    isStopOnWheel && Ele.removeEventListener('wheel', onWheel)
  }

  const stopScroll = (...args: any[]) => {
    Ele[resKeyName] && Ele[resKeyName].stop()
  }
  function wheelFn(event: EventTarget) {
    stopScroll(event)
  }
  function touchmoveFn(event: EventTarget) {
    stopScroll(event)
  }
  const onWheel = throttle(wheelFn, 200, true)
  const onTouchmove = throttle(touchmoveFn, 200, true)
  Ele[timer] && clearInterval(Ele[timer])
  removeEvent(Ele)
  addEvent(Ele)
  Ele[timer] = setInterval(() => {
    const nowScrollTop = Ele.scrollTop // 获取当前滚动条位置
    const tempStepDistance = (targetPosition - nowScrollTop) / step // 一步的长度
    const stepDistance =
      tempStepDistance > 0
        ? Math.ceil(tempStepDistance)
        : Math.floor(tempStepDistance)

    if (Ele.scrollTop === targetPosition) {
      clearInterval(Ele[timer])
      removeEvent(Ele)
      Ele[timer] = undefined
      Ele[resKeyName] = undefined
      typeof finish === 'function' && finish()
    }
    Ele.scrollTop = nowScrollTop + stepDistance
  }, spacingTime)

  const res = {
    stop: () => {
      Ele[timer] && clearInterval(Ele[timer])
      Ele[timer] = undefined
      Ele[resKeyName] = undefined
      removeEvent(Ele)
      return Ele.scrollTop
    }
  }
  Ele[resKeyName] = res
  return res
}

export { scrollUpOrDownWithAnimate }
